package com.prolificinteractive.materialcalendarview;

import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.PageSliderProvider;
import ohos.agp.text.Font;

import com.prolificinteractive.materialcalendarview.MaterialCalendarView.ShowOtherDates;
import com.prolificinteractive.materialcalendarview.ext.Logger2;
import com.prolificinteractive.materialcalendarview.format.DayFormatter;
import com.prolificinteractive.materialcalendarview.format.TitleFormatter;
import com.prolificinteractive.materialcalendarview.format.WeekDayFormatter;

import org.threeten.bp.LocalDate;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Pager adapter backing the calendar view
 */
abstract class CalendarPagerAdapter<V extends CalendarPagerView> extends PageSliderProvider {

    private final ArrayDeque<V> currentViews;

    protected MaterialCalendarView mcv;
    private final CalendarDay today;

    private TitleFormatter titleFormatter = TitleFormatter.DEFAULT;
    private Integer color = null;
    private Font  dateTextAppearance = null;
    private Font weekDayTextAppearance = null;
    private int showOtherDates = ShowOtherDates.SHOW_DEFAULTS.value;
    private CalendarDay minDate = null;
    private CalendarDay maxDate = null;
    private DateRangeIndex rangeIndex;
    private List<CalendarDay> selectedDates = new ArrayList<>();
    private WeekDayFormatter weekDayFormatter = WeekDayFormatter.DEFAULT;
    private DayFormatter dayFormatter = DayFormatter.DEFAULT;
    private DayFormatter dayFormatterContentDescription = dayFormatter;
    private List<DayViewDecorator> decorators = new ArrayList<>();
    private List<DecoratorResult> decoratorResults = null;
    private boolean selectionEnabled = true;
    boolean showWeekDays;
    Font font;
    Font fontweek;

    CalendarPagerAdapter(MaterialCalendarView mcv,Font font,Font fontweek) {
        this.mcv = mcv;
        this.today = CalendarDay.today();
        currentViews = new ArrayDeque<>();
        currentViews.iterator();
        setRangeDates(null, null);
        this.font=font;
        this.fontweek=fontweek;

    }

    // test
    CalendarPagerAdapter() {
        this.today = CalendarDay.today();
        currentViews = new ArrayDeque<>();
        setRangeDates(null, null);
    }

    public void setDecorators(List<DayViewDecorator> decorators,Font font) {
        this.decorators = decorators;
        invalidateDecorators(font);
    }

    public void invalidateDecorators(Font font) {
        decoratorResults = new ArrayList<>();
        for (DayViewDecorator decorator : decorators) {
            DayViewFacade facade = new DayViewFacade();
            decorator.decorate(facade);
            if (facade.isDecorated()) {
                decoratorResults.add(new DecoratorResult(decorator, facade));
            }
        }
        for (V pagerView : currentViews) {
            pagerView.setDayViewDecorators(decoratorResults,font);
        }
    }
    @Override
    public int getCount() {
        return rangeIndex.getCount();
    }

    public CalendarPagerAdapter<?> migrateStateAndReturn(CalendarPagerAdapter<?> newAdapter) {
        newAdapter.titleFormatter = titleFormatter;
        newAdapter.color = color;
        newAdapter.dateTextAppearance = dateTextAppearance;
        newAdapter.weekDayTextAppearance = weekDayTextAppearance;
        newAdapter.showOtherDates = showOtherDates;
        newAdapter.minDate = minDate;
        newAdapter.maxDate = maxDate;
        newAdapter.selectedDates = selectedDates;
        newAdapter.weekDayFormatter = weekDayFormatter;
        newAdapter.dayFormatter = dayFormatter;
        newAdapter.dayFormatterContentDescription = dayFormatterContentDescription;
        newAdapter.decorators = decorators;
        newAdapter.decoratorResults = decoratorResults;
        newAdapter.selectionEnabled = selectionEnabled;
        return newAdapter;
    }

    public int getIndexForDay(CalendarDay day) {
        if (day == null) {
            return getCount() / 2;
        }
        if (minDate != null && day.isBefore(minDate)) {
            return 0;
        }
        if (maxDate != null && day.isAfter(maxDate)) {
            return getCount() - 1;
        }
        return rangeIndex.indexOf(day);
    }

    protected abstract V createView(int position,Font fontweek);

    protected abstract int indexOf(V view);

    protected abstract boolean isInstanceOfView(Object object);

    protected abstract DateRangeIndex createRangeIndex(CalendarDay min, CalendarDay max);

    private static final int POSITION_NONE = -2;

    @Override
    public int getPageIndex(Object object) {
        if (!(isInstanceOfView(object))) {
            return POSITION_NONE;
        }
        CalendarPagerView pagerView = (CalendarPagerView) object;
        CalendarDay firstViewDay = pagerView.getFirstViewDay();
        if (firstViewDay == null) {
            return POSITION_NONE;
        }
        int index = indexOf((V) object);
        if (index < 0) {
            return POSITION_NONE;
        }
        return index;
    }

    @Override
    public Object createPageInContainer(ComponentContainer container, int position) {
        V pagerView = createView(position,fontweek);
        // pagerView.setAlpha(0);
        pagerView.setSelectionEnabled(selectionEnabled);

        pagerView.setWeekDayFormatter(weekDayFormatter);
        pagerView.setDayFormatter(dayFormatter);
        pagerView.setDayFormatterContentDescription(dayFormatterContentDescription);
        if (color != null) {
            pagerView.setSelectionColor(color);
        }
//        if (dateTextAppearance != null) {
//            pagerView.setDateTextAppearance(dateTextAppearance);
//        }
//        if (weekDayTextAppearance != null) {
//            pagerView.setWeekDayTextAppearance(weekDayTextAppearance);
//        }
        pagerView.setShowOtherDates(showOtherDates);
        pagerView.setMinimumDate(minDate);
        pagerView.setMaximumDate(maxDate);
        pagerView.setSelectedDates(selectedDates);
        pagerView.setBindStateChangedListener(new Component.BindStateChangedListener() {
            @Override
            public void onComponentBoundToWindow(Component component) {
                invalidateDecorators(font);
            }

            @Override
            public void onComponentUnboundFromWindow(Component component) {

            }
        });
        container.addComponent(pagerView);
        currentViews.add(pagerView);

        pagerView.setDayViewDecorators(decoratorResults,font);

        return pagerView;
    }


    public void setShowWeekDays(boolean showWeekDays) {
        this.showWeekDays = showWeekDays;
    }

    public boolean isShowWeekDays() {
        return showWeekDays;
    }

    public void setSelectionEnabled(boolean enabled) {
        selectionEnabled = enabled;
        for (V pagerView : currentViews) {
            pagerView.setSelectionEnabled(selectionEnabled);
        }
    }

    @Override
    public void destroyPageFromContainer(ComponentContainer componentContainer, int i, Object o) {
        V pagerView = (V) o;
        currentViews.remove(pagerView);
        componentContainer.removeComponent(pagerView);
    }

    @Override
    public boolean isPageMatchToObject(Component component, Object o) {
        return component == o;
    }

    public void setTitleFormatter(TitleFormatter titleFormatter) {
        this.titleFormatter = titleFormatter == null ? TitleFormatter.DEFAULT : titleFormatter;
    }

    public void setSelectionColor(int color) {
        this.color = color;
        for (V pagerView : currentViews) {
            pagerView.setSelectionColor(color);
        }
    }

    public void setDateTextAppearance(Font taId) {
        if (taId == null) {
            return;
        }
        this.dateTextAppearance = taId;
        for (V pagerView : currentViews) {
            pagerView.setDateTextAppearance(taId);
        }
    }
    public void setDateTextAppearanceSize(int size) {
        if (size == 0) {
            return;
        }
//        this.dateTextAppearance = taId;
        for (V pagerView : currentViews) {
            pagerView.setDateTextAppearanceSize(size);
        }
    }
    public void setShowOtherDates(int showFlags) {
        this.showOtherDates = showFlags;
        for (V pagerView : currentViews) {
            pagerView.setShowOtherDates(showFlags);
        }
    }

    public void setWeekDayFormatter(WeekDayFormatter formatter) {
        this.weekDayFormatter = formatter;
        for (V pagerView : currentViews) {
            pagerView.setWeekDayFormatter(formatter);
        }
    }

    public void setDayFormatter(DayFormatter formatter) {
        dayFormatterContentDescription = dayFormatterContentDescription == dayFormatter ?
            formatter : dayFormatterContentDescription;
        this.dayFormatter = formatter;
        for (V pagerView : currentViews) {
            pagerView.setDayFormatter(formatter);
        }
    }

    public void setDayFormatterContentDescription(DayFormatter formatter) {
        dayFormatterContentDescription = formatter;
        for (V pagerView : currentViews) {
            pagerView.setDayFormatterContentDescription(formatter);
        }
    }

    public int getShowOtherDates() {
        return showOtherDates;
    }

    public void setWeekDayTextAppearance(Font taId) {
        if (taId == null) {
            return;
        }
        this.weekDayTextAppearance = taId;
        for (V pagerView : currentViews) {
            pagerView.setWeekDayTextAppearance(taId);
        }
    }
    public void setWeekDayTextAppearanceSize(int size) {
        if (size != 0) {
            for (V pagerView : currentViews) {
                pagerView.setWeekDayTextAppearanceSize(size);
            }
        }
//        this.weekDayTextAppearance = size;
    }
    public void setRangeDates(CalendarDay min, CalendarDay max) {
        this.minDate = min;
        this.maxDate = max;
        for (V pagerView : currentViews) {
            pagerView.setMinimumDate(min);
            pagerView.setMaximumDate(max);
        }

        if (min == null) {
            min = CalendarDay.from(today.getYear() - 200, today.getMonth(), today.getDay());
        }

        if (max == null) {
            max = CalendarDay.from(today.getYear() + 200, today.getMonth(), today.getDay());
        }

        rangeIndex = createRangeIndex(min, max);
        notifyDataChanged();
        invalidateSelectedDates();
    }

    public DateRangeIndex getRangeIndex() {
        return rangeIndex;
    }

    public void clearSelections() {
        selectedDates.clear();
        invalidateSelectedDates();
    }

    /**
     * Select or un-select a day.
     *
     * @param day Day to select or un-select
     * @param selected Whether to select or un-select the day from the list.
     * @see CalendarPagerAdapter#selectRange(CalendarDay, CalendarDay)
     */
    public void setDateSelected(CalendarDay day, boolean selected) {
        if (selected) {
            if (!selectedDates.contains(day)) {
                selectedDates.add(day);
                invalidateSelectedDates();
            }
        } else {
            if (selectedDates.contains(day)) {
                selectedDates.remove(day);
                invalidateSelectedDates();
            }
        }
    }

    /**
     * Clear the previous selection, select the range of days from first to last, and finally
     * invalidate. First day should be before last day, otherwise the selection won't happen.
     *
     * @param first The first day of the range.
     * @param last The last day in the range.
     * @see CalendarPagerAdapter#setDateSelected(CalendarDay, boolean)
     */
    public void selectRange(final CalendarDay first, final CalendarDay last) {
        selectedDates.clear();

        // Copy to start from the first day and increment
        LocalDate temp = LocalDate.of(first.getYear(), first.getMonth(), first.getDay());

        // for comparison
        final LocalDate end = last.getDate();

        while (temp.isBefore(end) || temp.equals(end)) {
            selectedDates.add(CalendarDay.from(temp));
            temp = temp.plusDays(1);
        }

        invalidateSelectedDates();
    }

    private void invalidateSelectedDates() {
        validateSelectedDates();
        for (V pagerView : currentViews) {
            pagerView.setSelectedDates(selectedDates);
        }
    }

    private void validateSelectedDates() {
        for (int i = 0; i < selectedDates.size(); i++) {
            CalendarDay date = selectedDates.get(i);

            if ((minDate != null && minDate.isAfter(date)) || (maxDate != null
                && maxDate.isBefore(date))) {
                selectedDates.remove(i);
                mcv.onDateUnselected(date);
                i -= 1;
            }
        }
    }

    public CalendarDay getItem(int position) {
        return rangeIndex.getItem(position);
    }

    public List<CalendarDay> getSelectedDates() {
        return Collections.unmodifiableList(selectedDates);
    }

    protected Font  getDateTextAppearance() {
        return dateTextAppearance == null ? null : dateTextAppearance;
    }

    protected Font getWeekDayTextAppearance() {
        return weekDayTextAppearance == null ? null : weekDayTextAppearance;
    }
}
